package mx.kolobok.noteeditor.ui.list;

import mx.kolobok.noteeditor.Note;
import mx.kolobok.noteeditor.ui.Editor;
import mx.kolobok.noteeditor.ui.Icons;
import mx.kolobok.noteeditor.ui.UIUtils;
import mx.kolobok.noteeditor.ui.keymap.actions.NewNoteAction;
import mx.kolobok.noteeditor.ui.keymap.actions.NewNoteFromClipboardAction;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.ListUI;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.event.*;
import java.io.IOException;

/**
 * User:  Nikita Belenkiy
 * Date: 24.05.11
 * Time: 16:13
 */
public class NoteList extends JList {

    public static DataFlavor NOTE_DATA_FLAVOR;

    private static final Logger logger = Logger.getLogger(NoteList.class);
    protected NoteRenderer cellRenderer = new NoteRenderer();
    private NoteListModel noteListModel;

    static {
        try {
            NOTE_DATA_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=mx.kolobok.noteeditor.Note");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public NoteList(final Editor editor, NoteListModel noteListModel) {
        super(noteListModel);
        this.noteListModel = noteListModel;
        ListSelectionModel selectionModel = getSelectionModel();
        selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        selectionModel.addListSelectionListener(new NoteSelectionListener(editor));
        setCellRenderer(cellRenderer);
        addMouseListener(new RenameClickMouseListener());
        DeleteMouseListener mouseListener = new DeleteMouseListener();
        addMouseListener(mouseListener);
        addMouseMotionListener(mouseListener);

        UIUtils.addAll(this, JComponent.WHEN_FOCUSED);
        initDragAndDrop();
        setComponentPopupMenu(new JPopupMenu("New note"){
            {
                add(new NewNoteAction(NoteList.this.noteListModel));
                add(new NewNoteFromClipboardAction(NoteList.this.noteListModel));
            }

        });

    }

    private void initDragAndDrop() {
        setDragEnabled(true);
        setTransferHandler(new MyTransferHandler());
        DropTarget dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, new MyDropTargetAdapter(this));
        setAutoscrolls(true);
        MouseMotionListener doScrollRectToVisible = new MouseMotionAdapter() {
            public void mouseDragged(MouseEvent e) {
                Rectangle r = new Rectangle(e.getX(), e.getY(), 3, 3);
                ((JComponent) e.getSource()).scrollRectToVisible(r);
            }
        };
        addMouseMotionListener(doScrollRectToVisible);
    }


    public NoteListModel getNoteListModel() {
        return noteListModel;
    }

    private static class MyTransferHandler extends TransferHandler {
        @Override
        public int getSourceActions(JComponent c) {
            if (c instanceof NoteList) {
                return TransferHandler.MOVE;
            }
            return NONE;
        }

        @Override
        protected Transferable createTransferable(JComponent c) {
            NoteList list = (NoteList) c;
            Note selectedValue = (Note) list.getSelectedValue();
            return new NoteListTransferable(selectedValue);
        }

        private static class NoteListTransferable implements Transferable {

            private static final DataFlavor[] FLAVORS = new DataFlavor[2];
            private Note selectedValue;

            public NoteListTransferable(Note selectedValue) {

                this.selectedValue = selectedValue;
            }

            static {
                FLAVORS[0] = NOTE_DATA_FLAVOR;
                try {
                    FLAVORS[1] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=java.lang.String");
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public DataFlavor[] getTransferDataFlavors() {
                return FLAVORS;
            }

            @Override
            public boolean isDataFlavorSupported(DataFlavor flavor) {
                DataFlavor[] flavors = getTransferDataFlavors();
                for (int i = 0; i < flavors.length; i++) {
                    if (flavors[i].equals(flavor)) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
                Class<?> representationClass = flavor.getRepresentationClass();
                if (representationClass.equals(Note.class)) {
                    return selectedValue;
                } else if (representationClass.equals(String.class)) {
                    return selectedValue.toString();
                }
                return null;
            }
        }
    }


    private class NoteSelectionListener implements ListSelectionListener {
        private Editor editor;

        private NoteSelectionListener(Editor editor) {
            this.editor = editor;
        }

        public void valueChanged(@NotNull ListSelectionEvent e) {
            if (!e.getValueIsAdjusting()) {
                Note note = (Note) getSelectedValue();
                editor.setNote(note);
            }
        }
    }

    private class RenameClickMouseListener extends MouseAdapter {
        @Override
        public void mouseClicked(@NotNull MouseEvent event) {
            if (SwingUtilities.isLeftMouseButton(event)) {
                if (event.getClickCount() >= 2 &&  UIUtils.locationToIndex(NoteList.this, new Point(event.getX(), event.getY()))==getSelectedIndex()) {
                   SwingUtilities.processKeyBindings(new KeyEvent(event.getComponent(), KeyEvent.KEY_PRESSED,System.currentTimeMillis(),KeyEvent.CTRL_MASK,KeyEvent.VK_R,'R'));
                }
            }
        }


    }


    private class DeleteMouseListener extends MouseAdapter {
        private int prevMouseOverDelete = -1;
        @Override
        public void mouseMoved(MouseEvent e) {
            NoteList list = NoteList.this;
            ListUI ui = list.getUI();
            int index =UIUtils.locationToIndex(list,new Point(e.getX(),e.getY()));
            cellRenderer.mouseOverDelete = index !=-1&&isMouseOverDeleteButton(e.getX()) ? index : -1;

            if (prevMouseOverDelete != cellRenderer.mouseOverDelete) {

                if (prevMouseOverDelete != -1 && prevMouseOverDelete<noteListModel.getSize()/*checking that previous element still exists*/) {
                    Rectangle cellBounds = ui.getCellBounds(list, prevMouseOverDelete, prevMouseOverDelete);
                    list.repaint(cellBounds);
                }
                if (cellRenderer.mouseOverDelete != -1) {
                    Rectangle cellBounds = ui.getCellBounds(list, cellRenderer.mouseOverDelete, cellRenderer.mouseOverDelete);
                    list.repaint(cellBounds);
                }
                prevMouseOverDelete = cellRenderer.mouseOverDelete;
            }
        }

        @Override
        public void mouseExited(MouseEvent e) {
            int mouseOverDelete = cellRenderer.mouseOverDelete;
            if (mouseOverDelete != -1&&mouseOverDelete<noteListModel.getSize()/*checking that element still exists*/) {
                cellRenderer.mouseOverDelete = -1;
                NoteList list = NoteList.this;
                ListUI ui = list.getUI();
                Rectangle cellBounds = ui.getCellBounds(NoteList.this, prevMouseOverDelete, prevMouseOverDelete);
                list.repaint(cellBounds);
                prevMouseOverDelete = -1;
            }
        }

        @Override
        public void mouseClicked(MouseEvent event) {
            int x = event.getX();
            if (SwingUtilities.isLeftMouseButton(event)) {
                if (event.getClickCount() == 1) {
                    if (isMouseOverDeleteButton(x)) {
                        JComponent source = (JComponent) event.getSource();
//                      todo fix this crap
                        SwingUtilities.processKeyBindings(new KeyEvent(source, KeyEvent.KEY_PRESSED,System.currentTimeMillis(),KeyEvent.CTRL_MASK,KeyEvent.VK_D,'D'));
                    }
                }
            }
        }

        private boolean isMouseOverDeleteButton(int x) {
            int listWidth = NoteList.this.getWidth();
            return x > listWidth - Icons.deleteIconDefault.getIconWidth() && x < listWidth;
        }



    }
}
